package io;

import com.hp.hpl.jena.ontology.Individual;
import com.hp.hpl.jena.ontology.OntClass;
import com.hp.hpl.jena.ontology.OntModel;
import com.hp.hpl.jena.ontology.OntProperty;
import com.hp.hpl.jena.rdf.model.ModelFactory;
import com.hp.hpl.jena.rdf.model.Property;
import com.hp.hpl.jena.rdf.model.Resource;
import com.hp.hpl.jena.rdf.model.StmtIterator;
import com.hp.hpl.jena.rdf.model.impl.LiteralImpl;
import com.hp.hpl.jena.rdf.model.impl.StatementImpl;
import com.hp.hpl.jena.util.iterator.ExtendedIterator;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.mindswap.pellet.jena.PelletReasonerFactory;

/**
 *
 * @author HP
 */
public class OntologyManager {
     //ontology managed
    private OntModel ontology;
    //ontology source
    private String SOURCE;
    //ontology namespace
    private String NS;


    public OntologyManager(String source, String ns) {

        //SOURCE and NAMESPACE
        SOURCE = source;
        NS = ns;

        //create the model
        ontology = ModelFactory.createOntologyModel(PelletReasonerFactory.THE_SPEC);
        //load an ontology document into an ontology model
        ontology.read(SOURCE);
        ontology.prepare();
    }
    
    /**
     * Cierra la ontología.
     */
    public void cerrar() {
        ontology.close();
    }
    
    /**
     * Devuelve todas las Becas definidas en la ontología.
     * 
     * @return una lista con los nombres de todas las Becas.
     */
    public List<String> getBecas()
    {
        ArrayList<String> nombresBecas= new ArrayList<String>();
        OntClass becas = ontology.getOntClass(NS + "Beca");
        Iterator i = becas.listInstances();
        while (i.hasNext())
        {
            Individual individuo= (Individual) i.next();
            nombresBecas.add(individuo.getLocalName());
        }
        return nombresBecas;
    }
    
    /**
     * Devuelve los inscriptos existentes para una Beca.
     * 
     * @param nombreBeca: nombre de la Beca de la cual se quieren obtener sus inscriptos.
     * @return una lista con los nombres de los inscriptos definidos para una Beca.
     */
    public List<String> getConcursos(String nombreBeca)
    {
        ArrayList<String> nombresConcursos= new ArrayList<String>();
        OntProperty op= ontology.getOntProperty(NS + "para_una");
        OntClass concursos= ontology.getOntClass(NS + "Concurso");
        Iterator i= concursos.listInstances();
        while (i.hasNext())
        {
            Individual individuo= (Individual) i.next();
            if (individuo.getPropertyResourceValue(op).getLocalName().equals(nombreBeca))
                nombresConcursos.add(individuo.getLocalName());
        }
        return nombresConcursos;
    }
    
    /**
     * Devuelve los inscriptos de un concurso.
     * 
     * @param nombreConcurso: nombre del concurso del cual se quieren obtener los inscriptos.
     * @return una lista con los nombres de los individuos inscriptos al concurso.
     */
    public List<String> getInscriptos(String nombreConcurso)
    {
        ArrayList<String> nombresInscriptos= new ArrayList<String>();
        OntProperty op= ontology.getOntProperty(NS + "inscripto_a_un");
        OntClass inscriptos= ontology.getOntClass(NS + "Inscripto");
        Iterator instanciasIndividuos= inscriptos.listInstances();
        while (instanciasIndividuos.hasNext())
        {
            Individual individuo= (Individual) instanciasIndividuos.next();
            Iterator instanciasInscriptoAUn= individuo.listProperties(op);
            while (instanciasInscriptoAUn.hasNext())
            {
                StatementImpl propiedad= (StatementImpl) instanciasInscriptoAUn.next();
                if (propiedad.getResource().getLocalName().equals(nombreConcurso))
                    nombresInscriptos.add(individuo.getLocalName());
            }
        }
        return nombresInscriptos;
    }
    
    /**
     * Devuelve los postulantes de un concurso.
     * 
     * @param nombreConcurso: nombre del concurso del cual se quieren obtener sus postulantes.
     * @return una lista con los nombres de los individuos postulantes del concurso.
     */
    public List<String> getPostulantes(String nombreConcurso)
    {
        ArrayList<String> nombresPostulantes= new ArrayList<String>();
        OntProperty op= ontology.getOntProperty(NS + "postulante_a_un");
        OntClass postulantes= ontology.getOntClass(NS + "Postulante");
        Iterator instanciasIndividuos= postulantes.listInstances();
        while (instanciasIndividuos.hasNext())
        {
            Individual individuo= (Individual) instanciasIndividuos.next();
            Iterator instanciasPostulanteAUn= individuo.listProperties(op);
            while (instanciasPostulanteAUn.hasNext())
            {
                StatementImpl propiedad= (StatementImpl) instanciasPostulanteAUn.next();
                if (propiedad.getResource().getLocalName().equals(nombreConcurso))
                    nombresPostulantes.add(individuo.getLocalName());
            }
        }
        return nombresPostulantes;
    }
    
    /**
     * Devuelve el becario de una Beca de ayuda económica.
     * 
     * @param nombreConcurso nombre del concurso.
     * @return el nombre del individuo que es el becario.
     */
    public String getBecario(String nombreConcurso)
    {
        String ret= "(ninguno)";
        OntProperty op= ontology.getOntProperty(NS + "ganador_de_una");
        OntClass becario= ontology.getOntClass(NS + "Becario");
        Iterator i= becario.listInstances();
        while (i.hasNext())
        {
            Individual individuo= (Individual) i.next();
            if (individuo.getPropertyResourceValue(op).getLocalName().equals(nombreConcurso))
                ret= individuo.getLocalName();
        }
        return ret;
    }
    
    /**
     * Verifica si una Beca tiene asignado un becario.
     * 
     * @param nombreConcurso Nombre de uno de los concursos de dicha Beca.
     * @return True si la Beca posee un becario o False en cualquier otro caso.
     */
    public boolean poseeBecario(String nombreConcurso)
    {
        String resultado= this.getBecario(nombreConcurso);
        if (resultado.equals("(ninguno)"))
            return false;
        else
            return true;
    }
    
    /**
     * Retorna el valor de un dataProperty definido en la ontología.
     * 
     * @param clase nombre de la clase (dominio) del dataProperty.
     * @param individuoBeca nombre del individuoBeca del cual se quiere obtener el valor del dataProperty.
     * @param dataProperty nombre del dataProperty.
     * @return el valor solicitado o una cadena vacía si el nombre de la clase, del individuoBeca o del dataProperty no existe.
     */
    public String getDataPropertyValue(String clase, String individuo, String dataProperty)
    {
        OntClass ontClase = ontology.getOntClass(NS + clase);
        Iterator i= ontClase.listInstances();
        while (i.hasNext())
        {
            Individual instance = (Individual) i.next();
            if (instance.getLocalName().equals(individuo))
            {
                StmtIterator j= instance.listProperties();
                while (j.hasNext())
                {
                    Property prop= j.nextStatement().getPredicate();
                    if (prop.getLocalName().equals(dataProperty))
                        return this.getPropiedadValor(instance.listPropertyValues(prop));
                }
            }
        }
        return "";
    }
    
    /**
     * Transforma el valor de un dataProperty a un String.
     * 
     * @param e
     * @return una cadena con el valor del dataProperty.
     */
    private String getPropiedadValor(ExtendedIterator e) {
        String ret = "";
        while (e.hasNext())
        {
            Object j = e.next();
            if (j instanceof LiteralImpl)
                ret+= ((LiteralImpl) j).getValue();
            else
            {
                Resource val = (Resource) j;
                ret+= val.getLocalName();
                if (e.hasNext())
                    ret+= ", ";
            }
        }
        return ret;
    }
    
    /**
     * Retorna los pre-requisitos de una Beca.
     * 
     * @param nombreBeca un individuo de la clase "Beca" del cual se quieren obtener sus pre-requisitos.
     * @return una lista que contiene pares pre-requisito/valor.
     */
    public List<String []> getPreRequisitos(String nombreBeca)
    {
        ArrayList<String []> valoresPreRequisitos= new ArrayList<String []>();
        String nombrePreRequisito= this.getNombrePreRequisito(nombreBeca);
        OntClass ontClase= ontology.getOntClass(NS + "PreRequisitos");
        Iterator i= ontClase.listInstances();
        while (i.hasNext())
        {
            Individual preRequisito= (Individual) i.next();
            if (preRequisito.getLocalName().equals(nombrePreRequisito))
            {
                StmtIterator j= preRequisito.listProperties();
                while (j.hasNext())
                {
                    Property prop= j.nextStatement().getPredicate();
                    String nombrePropiedad= prop.getLocalName();
                    if (!nombrePropiedad.startsWith("type") && !nombrePropiedad.startsWith("sameAs"))
                    {
                        String [] partes= new String[2];
                        partes[0]= nombrePropiedad;
                        partes[1]= this.getPropiedadValor(preRequisito.listPropertyValues(prop));
                        valoresPreRequisitos.add(partes);
                    }
                }
            }
        }
        return valoresPreRequisitos;
    }
    
    /**
     * Retorna el nombre del individuo de la clase "PreRequisitos" relacionado con la Beca cuyo nombre
     * se recibe como parámetro.
     */
    private String getNombrePreRequisito(String nombreBeca)
    {
        String nombrePreRequisito= "";
        OntProperty op= ontology.getOntProperty(NS + "posee_prerequisitos");
        OntClass becas= ontology.getOntClass(NS + "Beca");
        Iterator i= becas.listInstances();
        while (i.hasNext())
        {
            Individual individuoBeca= (Individual) i.next();
            if (individuoBeca.getLocalName().equals(nombreBeca))
                nombrePreRequisito= individuoBeca.getPropertyResourceValue(op).getLocalName();
        }
        return nombrePreRequisito;
    }
    
    /**
     * Retorna los requisitos de un concurso.
     * 
     * @param nombreConcurso un individuo de la clase "Concurso" del cual se quieren obtener sus requisitos.
     * @return una lista que contiene pares requisito/valor.
     */
    public List<String []> getRequisitos(String nombreConcurso)
    {
        ArrayList<String []> valoresRequisitos= new ArrayList<String []>();
        String nombreRequisito= this.getNombreRequisito(nombreConcurso);
        OntClass ontClase= ontology.getOntClass(NS + "Requisitos");
        Iterator i= ontClase.listInstances();
        while (i.hasNext())
        {
            Individual requisito= (Individual) i.next();
            if (requisito.getLocalName().equals(nombreRequisito))
            {
                StmtIterator j= requisito.listProperties();
                while (j.hasNext())
                {
                    Property prop= j.nextStatement().getPredicate();
                    String nombrePropiedad= prop.getLocalName();
                    if (!nombrePropiedad.startsWith("type") && !nombrePropiedad.startsWith("sameAs"))
                    {
                        String [] partes= new String[2];
                        partes[0]= nombrePropiedad;
                        partes[1]= this.getPropiedadValor(requisito.listPropertyValues(prop));
                        valoresRequisitos.add(partes);
                    }
                }
            }
        }
        return valoresRequisitos;
    }

    /**
     * Retorna el nombre del individuo de la clase "Requisitos" relacionado con el concurso cuyo nombre
     * se recibe como parámetro.
     * 
     * @param nombreConcurso
     * @return 
     */
    private String getNombreRequisito(String nombreConcurso)
    {
        String nombreRequisito= "";
        OntProperty op= ontology.getOntProperty(NS + "posee_requisito");
        OntClass concursos= ontology.getOntClass(NS + "Concurso");
        Iterator i= concursos.listInstances();
        while (i.hasNext())
        {
            Individual individuoBeca= (Individual) i.next();
            if (individuoBeca.getLocalName().equals(nombreConcurso))
                nombreRequisito= individuoBeca.getPropertyResourceValue(op).getLocalName();
        }
        return nombreRequisito;
    }
    
    /**
     * Crea en la ontología la relación "primeroOrdenDeMerito" con el valor true para el postulante
     * que ganó la Beca.
     * 
     * @param nombrePostulante nombre del individuo que ganó la Beca.
     */
    public void setPrimeroOrdenMeritoTrue(String nombrePostulante)
    {
        Individual individuo= ontology.getIndividual(NS + nombrePostulante);
        OntProperty propiedad= ontology.getOntProperty(NS + "primeroOrdenDeMerito");
        individuo.addProperty(propiedad, ontology.createTypedLiteral(true));
    }
}
